package bufmgr;

import java.io.IOException;
import java.util.Hashtable;

import Queue.MyQueue;

import sun.misc.Queue;

import global.*;
import diskmgr.DB;
import diskmgr.DiskMgrException;
import diskmgr.FileIOException;
import diskmgr.InvalidPageNumberException;
import diskmgr.InvalidRunSizeException;
import diskmgr.Page;

public class BufMgr {
	private static DB myDB;
	private static byte[][] bufferBool;
	private static Descriptor[] bufDes;
	private byte policy = 1, clock = 1, LRU = 2;
	private int pointer ;
	private MyQueue emptyFrames ;
	Hashtable<PageId, Integer> g = new Hashtable<PageId, Integer>();

	public BufMgr(int numbuf, String replaceArgs) {
		
		myDB = new DB();
		bufferBool = new byte[numbuf][GlobalConst.MINIBASE_PAGESIZE];
		bufDes = new Descriptor[numbuf];
		pointer = 0;
		emptyFrames = new MyQueue();
	  if (replaceArgs.equalsIgnoreCase("LRU"))
			policy = LRU;

	}

	public void pinPage(PageId pin_pgid, Page page, boolean emptyPage) throws Exception {
		
		if(g.containsKey(pin_pgid)){//the page is in the buffer pool
			bufDes[g.get(pin_pgid)].pin_count++;
		}
		else{
			 find();
			if(pointer==-1)//The frame is full.How about full? I think all frames are pinned
				throw new Exception();
			ReadPage:
			{
			bufDes[pointer]=new Descriptor(pin_pgid, 1, false);
			bufferBool[pointer]=page.getpage();
			g.put(pin_pgid, pointer);
			}
		}
		
	}

	public void unpinPage(PageId PageId_in_a_DB, boolean dirty) throws Exception {
		if(bufDes[g.get(PageId_in_a_DB)].pin_count==0)throw new  Exception();// PageUnpinnedExceptio()
		bufDes[g.get(PageId_in_a_DB)].pin_count--;
		if(dirty)
		bufDes[g.get(PageId_in_a_DB)].dirtybit=true;
	}

	public PageId newPage(Page firstPage, int howMany) throws Exception {
		
             find();
		if (pointer == -1) {//The frame is full.How about full? I think all frames are pinned
         
			return null;
		} else {
			PageId pi = new PageId();
			
			myDB.allocate_page(pi, howMany);
			
			bufferBool[pointer] = firstPage.getpage();
			bufDes[pointer] = new Descriptor(pi, 0, false);
			pinPage(pi, firstPage, true);
			return pi;
		}
		
	}

	public void freePage(PageId globalPageId) throws InvalidRunSizeException, InvalidPageNumberException, FileIOException, DiskMgrException, IOException {
		int freeFrame =g.get(globalPageId);
		bufDes[freeFrame].pagenumber=null;//delete the page of the buffer .
		emptyFrames.enqueue(freeFrame);
		myDB.deallocate_page(globalPageId);
		
	}

	public void flushPage(PageId pageid) throws InvalidPageNumberException, FileIOException, IOException {
		
		myDB.write_page(pageid, new Page(bufferBool[g.get(pageid)]));
	}
/** 
 * find a page
 * @return the index of the first empty frame in the buffer pool 
 * @throws Exception 
 */
	private void find() throws Exception {
		if (pointer < bufDes.length - 1
				&& bufDes[pointer + 1].pagenumber != null
				&& emptyFrames.size() == 0) {// there is a frame next to pointer
			pointer++;
		} else {
			if (emptyFrames.size() > 0) {// there is empty frames but not next
											// to pointer.
				pointer = emptyFrames.dequeue();
			} else {
				pointer = -1;// no empty frames.
			}
		}
	}

	private class Descriptor {
		private PageId pagenumber;
		private boolean dirtybit;
		private int pin_count;

		public Descriptor(PageId id, int no, boolean dirt) {
			pagenumber = id;
			pin_count = no;
			dirtybit = dirt;
		}
	}
}
